#include "drv_spim.h"
#include <string.h>

#define DBG_TAG "drv.spim"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

uint8_t write_buffer[4096]; /* the content writed to spim flash */
uint8_t read_buffer[4096];  /* the content read from spim flash */

/**
 * @brief  init the spim flash
 * @param  none
 * @retval none
 */
void spim_init(void)
{
    gpio_init_type gpio_init_struct;

    /* enable the clock */
    crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
    crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);

    /* init spim io */
    gpio_default_para_init(&gpio_init_struct);
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
    gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
    gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
    gpio_init_struct.gpio_pins = GPIO_PINS_8;
    gpio_init(GPIOA, &gpio_init_struct);
    gpio_init_struct.gpio_pins = GPIO_PINS_1 | GPIO_PINS_6 | GPIO_PINS_7 | GPIO_PINS_10 | GPIO_PINS_11;
    gpio_init(GPIOB, &gpio_init_struct);

    /* enable spim, and select pb10, pb11 as spim io */
    gpio_pin_remap_config(EXT_SPIM_GMUX_1001, TRUE);

    /* in this example, use on-board en25qh128a as spim flash */
    flash_spim_model_select(FLASH_SPIM_MODEL1);

    /* unlock the spim flash program erase controller */
    while (flash_flag_get(FLASH_SPIM_OBF_FLAG))
        ;
    flash_spim_unlock();
    while (FLASH->ctrl3_bit.oplk)
        ;

    /* if the data written to spim flash need to be scrambled, please specify the scrambled range */
    flash_spim_encryption_range_set(0);

    return;
}

/**
 * @brief  read one sector from a specified address in spim flash
 * @param  address: specifies the sector starting address
 * @param  sector_size: sector size in bytes
 * @param  buf: the content read from spim flash
 * @retval none
 */
void sector_read(uint32_t address, uint32_t sector_size, uint8_t *buf)
{
    while (sector_size)
    {
        *(uint32_t *)buf = *(uint32_t *)address;
        sector_size -= sizeof(uint32_t);
        address += sizeof(uint32_t);
        buf += sizeof(uint32_t);
    }
}

/**
 * @brief  operate spim flash
 * @param  none
 * @retval none
 */
int spim_operate(int argc, char **argv)
{
    uint16_t i = 0;
    flash_status_type status = FLASH_OPERATE_DONE;

    /* configures the spim flash */


    LOG_D("spim init");

    /* fill the content to be writed to spim flash */
    for (i = 0; i < SPIM_SECTOR_SIZE; i++)
    {
        write_buffer[i] = i % 256;
    }

    /* wait for operation to be completed */
    status = flash_operation_wait_for(ERASE_TIMEOUT);
    LOG_D("status:%02X", status);
    if ((status == FLASH_PROGRAM_ERROR) || (status == FLASH_EPP_ERROR))
        flash_flag_clear(FLASH_PRGMERR_FLAG | FLASH_EPPERR_FLAG);
    else if (status == FLASH_OPERATE_TIMEOUT)
    {
        /* test spim fail */
        LOG_E("spim test fail 1");
        return -1;
    }

    dbg_here;
    /* erase an spim flash sector */
    status = flash_sector_erase(SPIM_TEST_ADDR);
    LOG_D("status:%02X", status);
    if (status != FLASH_OPERATE_DONE)
    {
        /* test spim fail */
        LOG_E("spim test fail 2");
        return -2;
    }
    // return 0;
    dbg_here;
    /* read an spim flash sector */
    memset(read_buffer, 0, SPIM_SECTOR_SIZE);
    sector_read(SPIM_TEST_ADDR, SPIM_SECTOR_SIZE, read_buffer);
    dbg_here;
    /* check if the desired sector are erased */
    for (i = 0; i < SPIM_SECTOR_SIZE; i++)
    {
        if (read_buffer[i] != 0xff)
        {
            /* test spim fail */
            LOG_E("spim test fail 3/%d",i);
            return -3;
        }
    }
    LOG_D("sector_read success");
    /* program an spim flash sector */
    i = 0;
    while (i < SPIM_SECTOR_SIZE)
    {
        status = flash_word_program(SPIM_TEST_ADDR + i, *(uint32_t *)(write_buffer + i));
        if (status != FLASH_OPERATE_DONE)
        {
            /* test spim fail */
            LOG_E("spim test fail 4.%d",i);
            return -4;
        }
        i = i + 4;
    }
    LOG_D("flash_word_program success");
    /* read an spim flash sector */
    memset(read_buffer, 0, SPIM_SECTOR_SIZE);
    sector_read(SPIM_TEST_ADDR, SPIM_SECTOR_SIZE, read_buffer);

    /* check if reading result and writing content are the same */
    for (i = 0; i < SPIM_SECTOR_SIZE; i++)
    {
        if (read_buffer[i] != write_buffer[i])
        {
            /* test spim fail */
            LOG_E("spim test fail 5/%d",i);
            return -5;
        }
    }
    LOG_D("spim test success");
    return RT_EOK;
}
uint8_t wb[SPIM_SECTOR_SIZE];
    uint8_t rb[SPIM_SECTOR_SIZE];
int spim_test_sector(uint32_t sector_addr)
{

    for(int i = 0; i < SPIM_SECTOR_SIZE; i++)
    {
        wb[i] = i % 256;
        rb[i] = 0;
    }

    /* wait for operation to be completed */
    int status = flash_operation_wait_for(ERASE_TIMEOUT);
    if ((status == FLASH_PROGRAM_ERROR) || (status == FLASH_EPP_ERROR))
        flash_flag_clear(FLASH_PRGMERR_FLAG | FLASH_EPPERR_FLAG);
    else if (status == FLASH_OPERATE_TIMEOUT)
    {
        /* test spim fail */
        LOG_E("spim test fail 1");
        return -1;
    }
    uint32_t dt = rt_tick_get();
    flash_sector_erase(sector_addr);
    dt = rt_tick_get() - dt;
    LOG_D("erase cost %d ms",dt);
    dt = rt_tick_get();
    sector_read(sector_addr, SPIM_SECTOR_SIZE, rb);
    dt = rt_tick_get() - dt;
    LOG_D("read cost %d ms",dt);
    for(int i = 0; i < SPIM_SECTOR_SIZE; i++)
    {
        if(rb[i] != 0xff)
        {
            LOG_E("e[%3d]:%02X fail",i,rb[i]);
            return -1;
        }
    }

    // return 0;
    dt = rt_tick_get();
    for(int i = 0; i < SPIM_SECTOR_SIZE; i+=4)
    {
        flash_word_program(sector_addr + i, *(uint32_t *)(wb + i));
    }
    dt = rt_tick_get() - dt;
    LOG_D("program cost %d ms",dt);
    memset(rb, 0, SPIM_SECTOR_SIZE);
    sector_read(sector_addr, SPIM_SECTOR_SIZE, rb);
    for(int i = 0; i < SPIM_SECTOR_SIZE; i++)
    {
        if(rb[i] != wb[i])
        {
            LOG_E("read2 fail at %d/0x%08X",i, sector_addr);
            return -2;
        }

    }
    return 0;
}
void spim_all(void)
{
    uint32_t dt = rt_tick_get();
    for(int i = 0; i < 64; i++)
    {
        uint32_t dt2 = rt_tick_get();
        uint32_t addr = i * SPIM_SECTOR_SIZE + SPIM_TEST_ADDR;
        int ret = spim_test_sector(addr);
        if(ret != 0)
        {
            LOG_E("s[%d-0x%08X] fail",i, addr);
        }else
        {
            LOG_D("s[%d-0x%08X] ok",i,addr);
        }
        dt2 = rt_tick_get() - dt2;
        LOG_D("s[%d-0x%08X] cost %d ms",i,addr,dt2);
    }
    dt = rt_tick_get() - dt;
    LOG_D("spim test sector all done, cost %d ms",dt);
}
MSH_CMD_EXPORT(spim_operate, spim operate);
MSH_CMD_EXPORT(spim_all, spim test sector all);

/**
 * @}
 */

/**
 * @}
 */
